#include "Logger.h"
#include "SocketsOps.h"
#include "Endian.h"

#include <arpa/inet.h>
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>


const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in* addr)
{
    return static_cast<const struct sockaddr*>((const void*)addr);
}
const struct sockaddr_in* sockets::sockaddr_in_cast(const struct sockaddr* addr)
{
    return static_cast<const struct sockaddr_in*>((const void*)(addr));
}
const struct sockaddr_in6* sockets::sockaddr_in6_cast(const struct sockaddr* addr)
{
    return static_cast<const struct sockaddr_in6*>((const void*)addr);
}

void sockets::fromIpPort(const char* ip, uint16_t port, struct sockaddr_in *addr)
{
    addr->sin_family = AF_INET;
    addr->sin_port = hostToNetwork16(port);
    if(::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0) // 点分十进制 -> binary
    {
        Logger::Error("sockets::fromIpPort");
    }
}

void sockets::fromIpPort(const char* ip, uint16_t port,
                         struct sockaddr_in6* addr)
{
    addr->sin6_family = AF_INET6;
    addr->sin6_port = hostToNetwork16(port);
    if (::inet_pton(AF_INET6, ip, &addr->sin6_addr) <= 0)
    {
        Logger::Error("sockets::fromIpPort");
    }
}

void sockets::toIp(char* buf, size_t size, const struct sockaddr *addr) 
{
    if(addr->sa_family == AF_INET) 
    {
        assert(size >= INET_ADDRSTRLEN);
        const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);
        ::inet_ntop(AF_INET, addr4, buf, static_cast<socklen_t>(size));
    }
    else if(addr->sa_family == AF_INET6)
    {
        assert(size >= INET6_ADDRSTRLEN);
        const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);
        ::inet_ntop(AF_INET6, addr6, buf, static_cast<socklen_t>(size));
    }
}

void sockets::toIpPort(char* buf, size_t size,
              const struct sockaddr* addr)
{
    if(addr->sa_family == AF_INET6)
    {
        buf[0] = '[';
        toIp(buf + 1, size - 1, addr);
        const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);
        uint16_t port = networkToHost16(addr6->sin6_port);
        size_t end = ::strlen(buf);
        assert(size > end);
        snprintf(buf + end, size - end, "]:%u", port);
        return;
    }
    toIp(buf, size, addr);
    const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);
    uint16_t port = networkToHost16(addr4->sin_port);
    size_t end = ::strlen(buf);
    assert(size > end);
    snprintf(buf + end, size - end, ":%u", port);
}
